package com.magiccompass.alipay.barcode.signature;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import java.util.Base64;

import com.magiccompass.alipay.barcode.constants.AlipayPartnerConstants;
import com.magiccompass.alipay.barcode.exception.StandardException;
import com.magiccompass.alipay.barcode.util.StringUtil;
import com.magiccompass.alipay.barcode.util.StreamUtil;

public class RSASignatureValidation {
	public static boolean rsaCheck(Map<String, String> params, String publicKey, String charset) throws Exception {
		String sign = params.get("sign");
		String content = getSignCheckContent(params);
		
		return rsaCheckContent(content, sign, publicKey, charset);
	}

	private static String getSignCheckContent(Map<String, String> params) {
        if (params == null) {
            return null;
        }
        params.remove("sign");
        params.remove("sign_type");
        
        StringBuffer content = new StringBuffer();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            content.append((i == 0 ? "" : "&") + key + "=" + value);
        }
        return content.toString();
    }	
	
	private static boolean rsaCheckContent(String content, String sign, String publicKey,String charset) throws Exception {
		try {
		PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
		
		java.security.Signature signature = java.security.Signature.getInstance(AlipayPartnerConstants.SIGN_ALGORITHMS);
		
		signature.initVerify(pubKey);
		
		if (StringUtil.isEmpty(charset)) {
			signature.update(content.getBytes());
		} else {
			signature.update(content.getBytes(charset));
		}
		
			return signature.verify(Base64.getDecoder().decode(sign.getBytes()));
		} catch (Exception e) {
			throw new StandardException("RSAcontent:" + content + ",sign:" + sign+ ",charset:" + charset, e);
		}
	}
	
	private static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
		KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
		
		StringWriter writer = new StringWriter();
		StreamUtil.io(new InputStreamReader(ins), writer);
		
		byte[] encodedKey = writer.toString().getBytes();
		
		encodedKey = Base64.getDecoder().decode(encodedKey);
		
		return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
	}
}
